package com.lyx.rpc.provider.common.handler;

import com.lyx.rpc.common.helper.RpcServiceHelper;
import com.lyx.rpc.common.threadpool.ServerThreadPool;
import com.lyx.rpc.constants.RpcConstants;
import com.lyx.rpc.protocol.RpcProtocol;
import com.lyx.rpc.protocol.enumeration.RpcStatus;
import com.lyx.rpc.protocol.enumeration.RpcType;
import com.lyx.rpc.protocol.header.RpcHeader;
import com.lyx.rpc.protocol.request.RpcRequest;
import com.lyx.rpc.protocol.response.RpcResponse;
import com.lyx.rpc.reflect.api.ReflectInvoker;
import com.lyx.rpc.spi.loader.ExtensionLoader;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastMethod;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @Author by lyx
 * RPC服务提供者的Handler处理类
 */
public class RpcProviderHandler extends SimpleChannelInboundHandler<RpcProtocol<RpcRequest>> {
    private final Logger logger = LoggerFactory.getLogger(RpcProviderHandler.class);
    //存储服务名称#版本号#分组与对象实例的映射关系
    private final Map<String, Object> handlerMap;
    private ReflectInvoker reflectInvoker;

    public RpcProviderHandler(String reflectType, Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
        this.reflectInvoker = ExtensionLoader.getExtension(ReflectInvoker.class, reflectType);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcProtocol<RpcRequest> rpcRequestRpcProtocol) throws Exception {
        ServerThreadPool.submit(() -> {
//
//            logger.info("RPC提供者收到的数据为====>>> {}", JSONObject.toJSONString(rpcRequestRpcProtocol));
//            logger.info("handlerMap中存放的数据如下所示：");
//            for (Map.Entry<String, Object> entry : handlerMap.entrySet()) {
//                logger.info(entry.getKey() + " === " + entry.getValue());
//            }
            RpcHeader header = rpcRequestRpcProtocol.getHeader();
            RpcRequest requestBody = rpcRequestRpcProtocol.getBody();
            logger.debug("接收的请求 id 为：{}", header.getRequestId());
            // 将header中的消息类型设置为响应类型的消息
            header.setMsgType((byte) RpcType.RESPONSE.getType());
            // 构建响应数据
            RpcProtocol<RpcResponse> rpcResponseRpcProtocol = new RpcProtocol<>();
            RpcResponse response = new RpcResponse();
            try {
                Object result = handle(requestBody);
                response.setResult(result);
                response.setAsync(requestBody.getAsync());
                response.setOneway(requestBody.getOneway());
                header.setStatus((byte) RpcStatus.SUCCESS.getCode());
            } catch (Throwable t) {
                header.setStatus((byte) RpcStatus.FAIL.getCode());
                response.setResult(t);
                logger.error("服务提供者无法处理请求：", t);
            }
            rpcResponseRpcProtocol.setHeader(header);
            rpcResponseRpcProtocol.setBody(response);
            channelHandlerContext.writeAndFlush(rpcResponseRpcProtocol).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture channelFuture) throws Exception {
                    logger.debug("请求id为：{} 得到响应", header.getRequestId());
                }
            });
        });
    }

    private Object handle(RpcRequest request) throws Throwable {
        String serviceKey = RpcServiceHelper.buildServiceKey(request.getClassName(), request.getVersion(), request.getGroup());
        Object serviceBean = handlerMap.get(serviceKey);
        if (serviceBean == null) {
            throw new RuntimeException(String.format("service not exist: %s:%s", request.getClassName(), request.getMethodName()));
        }
        Class<?> serviceClass = serviceBean.getClass();
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParameters();
        logger.debug(serviceClass.getName());
        logger.debug(methodName);
        if (parameterTypes != null && parameterTypes.length > 0) {
            for (int i = 0; i < parameterTypes.length; ++i) {
                logger.debug(parameterTypes[i].getName());
            }
        }
        if (parameters != null && parameters.length > 0) {
            for (int i = 0; i < parameters.length; ++i) {
                logger.debug(parameters[i].toString());
            }
        }
        return this.reflectInvoker.invokeMethod(serviceBean, serviceClass, methodName, parameterTypes, parameters);
    }

}
